#!/bin/bash
#
# This script will perform encrypted backups of a bunch of files or a folder.
#
# A backup is defined as:
# - A copy of a file that has been compressed as to occupy less space than the
#   original (an archive)
# - A way of adding more files to said archive as you wish, thus allowing the
#   archive to grow as needed, depending on whether more files come in.
#
# It relies heavily upon GPG for its business
#
# Development roadmap:
# - Allow opening an encrypted file, adding new content and closing it again
# - Allow the distinction between files and folders for backup.
# - Allow changing the recipient for the backup
# - Allow a rudimentary versioning system.
# - Implement a preferences system for the script, where a user can change the
#   default archive format (ex Zip, tar.bz2, 7z), etc.
#
#
#    Copyright (C) 2016 - Klaus Zimmermann - https://quitter.se/kzimmermann
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# The software version:
VERSION="0.4 - alpha"

# We now use a config dotfile in the user's home folder:
CONFIG="$HOME/.backuprc"

if [[ ! -f "$CONFIG" ]]
then
    echo "Error: configuration file not found at $CONFIG"
    echo "Please copy the backuprc file into your home folder and try again."
    exit 1
fi

while read line
do
    if [[ -z $(echo "$line" | grep -E '^#' ) ]]
    then
        if [[ "$line" == "" ]]
        then
            continue
        else
            key=$(echo "$line" | cut -d "=" -f 1)
            case "$key" in
                "recipient" )
                    recipient=$(echo "$line" | cut -d "=" -f 2)
                ;;
                "autosign" )
                    SIGN=$(echo "$line" | cut -d "=" -f 2)
                ;;
                * )
                    echo "Skipping unknown configuration option '$key'"
                ;;
            esac
        fi
    fi
done < $CONFIG

# State variables to be changed by the arguments. Do not edit manually.
FILENAME=""
ACTION=""

# check dependencies just in case:
if [[ -z "$(which gpg)" ]]
then
    echo "Error: please install gpg to use this script"
    exit 1
fi

if [[ -z "$(which tar)" ]]
then
    echo "Error: please install tar to use this script"
    exit 1
fi

if [[ -z "$recipient" ]]
then
    echo "Error: no recipient specified."
    echo "Please indicate a recipient in the 'recipient= ' line and try again."
    echo "Note that this recipient must be listed in your GPG keyring"
    exit 1
fi

# Functions used by this script:

helper() {
    cat <<EOF_HELPER
Usage: $(basename $0) COMMAND FOLDER [OPTION]
Commands are:
-e, --encrypt: creates an encrypted archive from FOLDER to the recipient
-d, --decrypt: attempts to decrypt the encrypted archive FOLDER

Available options are:
-h, --help: shows this help message
-s, --sign: also signs the encrypted package with your main private key

The recipient chosen must be listed in your GPG keyring as well.
The current recipient is: $recipient
EOF_HELPER
    gpg --list-keys $recipient
    echo "Version: $VERSION"
}

encrypt() {
    if [[ -z "$1" ]]
    then
        echo "Please select a folder to back up and encrypt."
        exit 1
    elif [[ ! -d "$1" ]]
    then
        echo "Error: '$1' either doesn't exist or isn't a folder. $(basename $0) can only back up folders."
        echo "Please put your desired files in a folder and try again with that folder."
        exit 1
    fi
    local filename="$(basename "$1")$(date +%Y%m%d).tar.gz"
    printf "Now encrypting folder '$1' to recipient $recipient ... "
    tar -czvf "$filename" $1
    if [[ "$SIGN" == true ]]
    then
        gpg -r "$recipient" --sign --encrypt "$filename" 
    else
        gpg -r "$recipient" --encrypt "$filename" 
    fi
    shred -u "$filename"
    printf "Finished\n"
}

decrypt() {
    local finalfilename=""
    local testname="$(echo ${1/.asc/})"
    if [[ "$1" == "$testname" ]]
    then
        # file might be .gpg
        finalfilename="$(echo ${1/.gpg/})"
    else
        finalfilename="$(echo ${1/.asc/})"
    fi
    echo "Attempting to decrypt file '$1' using $recipient's key..."
    gpg --decrypt "$1" > "$finalfilename" || echo "Failed to decrypt the file."
    tar -xzvf "$finalfilename"
    shred -u "$finalfilename"
}


# Check command-line arguments:
if [[ "$#" == 0 ]]
then
    echo "Missing arguments."
    helper
    exit 1
else
    while [[ -n $1 ]]
    do
        case "$1" in
            "-h" | "--help" ) 
                helper; exit 0 
            ;;
            "-e" | "--encrypt" ) 
                if [[ -z "$ACTION" ]]
                then
                    ACTION="encrypt"
                    shift
                    FILENAME="$1"
                else
                    echo "Error: cannot encrypt and decrypt at the same time!"
                    exit 1
                fi
            ;;
            "-d" | "--decrypt" ) 
                if [[ -z "$ACTION" ]]
                then
                    ACTION="decrypt"
                    shift
                    FILENAME="$1"
                else
                    echo "Error: cannot encrypt and decrypt at the same time!"
                    exit 1
                fi
            ;;
            "-s" | "--sign" )
                SIGN=true
            ;;
            * ) echo "Invalid arguments. Please see '$(basename $0) -h'" ;;
        esac
        shift
    done
fi

if [[ "$ACTION" == "encrypt" ]]
then
    encrypt "$FILENAME"
elif [[ "$ACTION" == "decrypt" ]]
then
    decrypt "$FILENAME"
else
    echo "Error: missing arguments."
    helper
    exit 1
fi

exit 0
